package com.beanfactorypostprocessor;

import com.config.SqlConfig;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.Ordered;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.core.type.MethodMetadata;
import org.springframework.core.type.classreading.CachingMetadataReaderFactory;
import org.springframework.core.type.classreading.MetadataReader;
import org.springframework.util.StringUtils;

import java.io.IOException;
import java.util.Arrays;
import java.util.Map;
import java.util.Set;

import static org.springframework.beans.factory.config.AutowireCapableBeanFactory.AUTOWIRE_CONSTRUCTOR;

/**
 * 用于解析@Configuration注解标记的配置类，并将其作为工厂方法(FactoryBean)来解析 @Bean
 */
@Slf4j
public class FactoryBean_BeanFactoryPostProcessor implements BeanDefinitionRegistryPostProcessor, Ordered {

    CachingMetadataReaderFactory readerFactory = new CachingMetadataReaderFactory();

    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {

//        Map<String, Object> beansWithAnnotation = beanFactory.getBeansWithAnnotation(Configuration.class);

        log.info("-------->执行postProcessBeanFactory");
    }

    /**
     * 在所有 BeanDefinition 添加到 BeanFactory 之后，且在 Bean 实例化之前，允许继续往 BeanFactory 中进一步添加 BeanDefinition
     * @param registry
     * @throws BeansException
     */
    @Override
    public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
        log.info("-------->执行postProcessBeanDefinitionRegistry");


       /* Arrays.stream(registry.getBeanDefinitionNames()).map(registry::getBeanDefinition).map(beanDefinition -> {
            beanDefinition.getBeanClassName()
        })*/

        MetadataReader metadataReader = null;
        try {
            metadataReader = readerFactory.getMetadataReader(new ClassPathResource("com/config/SqlConfig.class"));
        } catch (IOException e) {
            e.printStackTrace();
            throw new RuntimeException(e);
        }

//        获取注解元数据
        AnnotationMetadata annotationMetadata = metadataReader.getAnnotationMetadata();

//        获取被注解标注的方法
        Set<MethodMetadata> annotatedMethods = annotationMetadata.getAnnotatedMethods(Bean.class.getName());

        String sqlConfigBeanName = "sqlConfig";


        annotatedMethods.forEach(annotatedMethod -> {

            System.out.println(annotatedMethod);

            BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder.genericBeanDefinition();

            Object initMethod = annotatedMethod.getAnnotationAttributes(Bean.class.getName()).get("initMethod");

            if (initMethod != null && StringUtils.hasLength(initMethod.toString())) {
//                设置bean初始化之后要执行的方法
                beanDefinitionBuilder.setInitMethodName(initMethod.toString());
            }


            AbstractBeanDefinition beanDefinition = beanDefinitionBuilder
//                    setFactoryMethodOnBean() 第一个参数是工厂方法的名字
//                    第二个参数是这个工厂方法所在的工厂对象在BeanFactory中的名字
                    .setFactoryMethodOnBean(annotatedMethod.getMethodName(), sqlConfigBeanName)
//                    设置Autowire 模式，默认没有开启自动装配
//                    因为 SqlSessionFactory 方法依赖了 DataSource 依赖作为参数，不开启自动装配将导致失败
                    .setAutowireMode(AUTOWIRE_CONSTRUCTOR)
                    .getBeanDefinition();

//            一般使用工厂方法的名字作为 bean 的名字
            registry.registerBeanDefinition(annotatedMethod.getMethodName(), beanDefinition);

        });
    }

    @Override
    public int getOrder() {
        return LOWEST_PRECEDENCE;
    }
}
